const Decimal = require('decimal.js')
const {
  OrderDirectionFactor,
  BuildConfig
} = require('./../constants')

const fnFixRate = (str, fixNum = 6) => {
  return Number(Number(str).toFixed(fixNum))
}

const fnFixPrice = (str, fixNum = 2) => {
  return Number(Number(str).toFixed(fixNum))
}

class MathModel {
  constructor () {
    let modelInfo = {
      // 容忍浮动，币价的0.02
      tolerentPriceDiffRate: 0.015,
      // 大仓位分仓数
      bigHoldingDivNum: 4,
      // 小仓位分仓数
      smallHoldingDivNum: 5,
      // 首次建仓比例
      firtBuildRatio: 0.032,
      // 初始计算比例
      initCountRatio: 0.00145,
      // 仓位比例递增
      holdingIncrease: 4,
      // 每次开仓数量
      // 13 - 16.8%，720
      buildNums: BuildConfig.BUILD_NUM,
      // 后面救仓数量，最大15
      // 12 - 19.98%，449,
      // 16 - 28.97%，515,
      // 20 - 40.28%，581,
      // 25 - 57.68%，664
      // 30 - 78.71%，747
      // 34 - 98.14%, 813
      saveNums: 16
    }

    this.modelInfo = modelInfo
  }
  getModelInfo () {
    return this.modelInfo
  }
  getBuildingList ({openPrice, direction, leverage, freeAmount, protectModeIndex = 15, customSaveNums = 0}) {
    const modelInfo = this.modelInfo
    // 价格间隔
    modelInfo.tolerentPriceDiff = modelInfo.tolerentPriceDiffRate * openPrice
    // 大仓位价格间隔
    modelInfo.bigHoldingPriceGap = Decimal(modelInfo.tolerentPriceDiff).div(modelInfo.bigHoldingDivNum).toNumber()
    // 小仓位价格间隔
    modelInfo.smallHoldingPriceGap = Decimal(modelInfo.bigHoldingPriceGap).div(modelInfo.smallHoldingDivNum).toNumber()
    const {
      firtBuildRatio,
      initCountRatio,
      smallHoldingPriceGap,
      holdingIncrease,
      smallHoldingDivNum,
      buildNums,
      saveNums: _saveNums
    } = this.modelInfo

    const saveNums = customSaveNums || _saveNums
    
    // 快速得出建仓价格数组，如[58000, 58060, 58120, 58180, 58240, 58300, 58360, 58420, 58480, 58540, 58600, 58660, 58720, 58780, 58840, 58900, 58960, 59020]
    let buildPriceList = Array.from(Array(buildNums)).map((e, i) => fnFixPrice(openPrice + i * smallHoldingPriceGap * OrderDirectionFactor[direction]))
    const lastBuildPrice = buildPriceList[buildPriceList.length - 1]
    const savePriceList = Array.from(Array(saveNums)).map((e, i) => lastBuildPrice + (smallHoldingPriceGap + i * smallHoldingPriceGap * 0.8) * OrderDirectionFactor[direction])
    buildPriceList = [...buildPriceList, ...savePriceList]

    // 算出建仓价格
    let buildList = buildPriceList.map((item, index) => {
      const buildItem = {
        buildPrice: item
      }

      let basicRatio = 0
      switch (index) {
        case 0:
          buildItem.buildRatio = fnFixRate((firtBuildRatio - initCountRatio) / 2)
          buildItem.buildAvgPrice = fnFixPrice(openPrice)
          buildItem.totalBuildRatio = fnFixRate(buildItem.buildRatio)
          buildItem.costGap = 0
          buildItem.maxCostGap = 0
          break;
        case 1:
          buildItem.buildRatio = fnFixRate(initCountRatio)
          break;
        case 2:
          buildItem.buildRatio = fnFixRate((firtBuildRatio - initCountRatio) / 6)
          break;
        case 3:
        case 4:
        case 5:
        case 6:
        case 7:
          basicRatio = initCountRatio * holdingIncrease
          buildItem.buildRatio = fnFixRate((basicRatio / smallHoldingDivNum / (smallHoldingDivNum + 1) * (index + 1)) + ((firtBuildRatio - initCountRatio) / 12))
          break;
        case 8:
        case 9:
        case 10:
        case 11:
        case 12:
          basicRatio = initCountRatio * holdingIncrease * (holdingIncrease + 1)
          buildItem.buildRatio = fnFixRate(basicRatio / smallHoldingDivNum / (smallHoldingDivNum + 1) * (index - 4))
          break;
        default:
          buildItem.buildRatio = initCountRatio * (index - 11)
          break;
      }
        
      return buildItem
    })


    // 统一处理算出总建仓比例和均价
    for (let i = 0; i <= buildList.length - 1; i++) {
      const buildItem = buildList[i]
      if (i > 0) {
        const lastItem = buildList[i - 1]
        buildItem.totalBuildRatio = fnFixRate(lastItem.totalBuildRatio + buildItem.buildRatio)
        buildItem.buildAvgPrice = fnFixPrice((lastItem.buildAvgPrice * lastItem.totalBuildRatio + buildItem.buildPrice * buildItem.buildRatio) / buildItem.totalBuildRatio)
        buildItem.costGap = fnFixPrice(Math.abs(buildItem.buildAvgPrice - buildItem.buildPrice) * -1)
        buildItem.maxCostGap = fnFixPrice(buildItem.costGap - smallHoldingPriceGap)
    
      }
      // 交易量
      const usdtQuantity = Decimal(leverage)
        .mul(freeAmount)
        .mul(buildItem.buildRatio)
        .toNumber()
      buildItem.btcQuantity = Math.max(Number(Decimal(usdtQuantity)
        .div(openPrice).toFixed(3))
        , 0.001)
      buildItem.direction = direction
      // 第二项特殊处理
      if (i === 1) {
        buildItem.btcQuantity = buildItem.btcQuantity + 0.001
      }
    }

    const _buildList = buildList.map((item, index) => {
      // 计算每仓成交后均价
      const realAvgPrice = this.getAvgPrice(buildList, index + 1)
      let protectInfo = {}
      if (index >= protectModeIndex) {
        // 保护止盈价
        protectInfo.stopPrice = fnFixPrice(Decimal(realAvgPrice).sub(OrderDirectionFactor[item.direction] * openPrice * 0.002).toNumber())
        // 保护止盈量
        protectInfo.stopQuantity = this.getStopQuantity(buildList, 0, index)
        // 保护止盈后均价
        protectInfo.stopAvgPrice = item.buildAvgPrice
        // 保护止盈后拉高均价
        protectInfo.stopAvgIncrease = item.buildAvgPrice
      }
      
      return {
        ...item,
        realAvgPrice,
        protectInfo
      }
    })

    return _buildList
  }
  /**
   * 获取建仓均价
   * @param {Array} buildList 开单列表
   * @param {Number} buildNum 成交仓数
   * @returns 
   */
  getAvgPrice (buildList, buildNum) {
    const priceVolList = buildList.slice(0, buildNum).map(item => ([Decimal(item.btcQuantity).mul(item.buildPrice).toNumber(), item.btcQuantity]))
    let totalVolumn = 0
    let totalBtcQuantity = 0
    priceVolList.forEach(([volumn, btcQuantity]) => {
      totalVolumn = Decimal(totalVolumn).add(volumn).toNumber()
      totalBtcQuantity = Decimal(totalBtcQuantity).add(btcQuantity).toNumber()
    })
    return fnFixPrice(Decimal(totalVolumn).div(totalBtcQuantity).toNumber())
  }
  getStopQuantity (buildList, protectModeIndex, index) {
    const btcQuantityList = buildList.slice(protectModeIndex, index + 1).map(item => item.btcQuantity)
    let totalBtcQuantity = 0
    btcQuantityList.forEach(btcQuantity => {
      totalBtcQuantity = Decimal(totalBtcQuantity).add(btcQuantity).toNumber()
    })
    return totalBtcQuantity
  }
  get fnFixPrice () {
    return fnFixPrice
  }
}

module.exports = new MathModel()